#include <iostream> 
#include <ctime> 
#include <windows.h> 
#include <conio.h> 

using namespace std;

HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

void MoveCursor(int X, int Y)
{
	COORD coord = { X, Y };
	SetConsoleCursorPosition(hConsole, coord);
} 

enum ConsoleColor
{
    	Black         = 0,
		Blue          = 1,
		Green         = 2,
		Cyan          = 3,
		Red           = 4,
		Magenta       = 5,
		Brown         = 6,
		LightGray     = 7,
		DarkGray      = 8,
		LightBlue     = 9,
		LightGreen    = 10,
		LightCyan     = 11,
		LightRed      = 12,
		LightMagenta  = 13,
		Yellow        = 14,
		White         = 15
};

void SetColor(ConsoleColor text, ConsoleColor background)
{
    SetConsoleTextAttribute(hConsole, (WORD)((background << 4) | text));
}

struct Snake 
{
	COORD *t;	
	int Length;
};

enum Direction {LEFT, UP, RIGHT, DOWN};

struct Game
{
	Snake snake;
	COORD apple; 
	int dx, dy;  
	int speed;  
	int direct; 
};

void AddApple(Game & game) 
{
	int i, x, y;
	int n = game.snake.Length;
	do
	{
		x = rand()%56 + 3; 
		y = rand()%19 + 3; 
		for(i = 0; i < n; i++)
		{
			if(x == game.snake.t[i].X && y == game.snake.t[i].Y) break; // checking to ensure that the apple does not coincide with snake
		}
	}
	while(i < n);

	game.apple.X = x;
	game.apple.Y = y; 
	SetConsoleCursorPosition(hConsole, game.apple);
	SetConsoleTextAttribute(hConsole, 0x0c); 
	printf("%c", 4);
	
}

void StartGame(Game & game)
{
	system("cls");
	game.snake.Length = 3;
	game.snake.t = new COORD [3];

	for(int i = 0; i < 3; i++)
	{
		game.snake.t[i].X = 20 + i;
		game.snake.t[i].Y = 20;
	}

	game.dx = 1;
	game.dy = 0;
	game.speed = 100;
	AddApple(game);
}

void Show()
{
	SetColor(LightBlue, Black);

	MoveCursor(64, 2);cout <<  "STATS" << endl;
	MoveCursor(64, 3); cout << "\tApples: 0" << endl;
	MoveCursor(64, 4); cout << "\tLength: 3"<< endl;
	MoveCursor(64, 5); cout << "\tSpeed:  1" << endl;
	MoveCursor(64, 7); cout << "Controls" << endl; 
    MoveCursor(64, 8); cout << "\tEsc - Exit" << endl;
	MoveCursor(64, 9); cout << "\tP   - Pause/Unpause" << endl;   
	MoveCursor(64, 12);
	cout << "\t" << "Arrow Up    - Turn Up" << endl;
	MoveCursor(64, 13);
	cout << "\t" << "Arrow Down  - Turn Down" << endl;
	MoveCursor(64, 14);
	cout << "\t" << "Arrow Left  - Turn Left" << endl;
	MoveCursor(64, 15);
	cout << "\t" << "Arrow Right - Turn Right" << endl;

	short m = 0, h = 3;
	SetColor(LightMagenta, Black);
				
    MoveCursor(2, 2);
	for(m = 0; m < 60; m++)
	{
		cout << "*";
	}
	
	MoveCursor(2, 24);
	for(m = 0; m < 60; m++)
	{
		cout << "*";
	}

	while(h <= 23)
	{
		MoveCursor(2, h);
		cout << "*" << endl;
		++h;
	}
	
	h = 3;
	while(h <= 23)
	{
		MoveCursor(61, h);
		cout << "*" << endl;
		++h;
	}
	
}

enum {FINISH, WALL, APPLE, MOVE}; // Results of working Move()

int Move(Game & game)
{
	int & count = game.snake.Length;

	COORD head = game.snake.t[count - 1];
	COORD tail = game.snake.t[0];
	COORD next;

	next.X = head.X + game.dx;
	next.Y = head.Y + game.dy;
	
    if(next.X < 3 || next.Y < 3 || next.X > 60 || next.Y > 23)
		return WALL;
		
	if(count > 4)
	{
		for(int i = 0; i < count; i++)
			if(next.X == game.snake.t[i].X && next.Y == game.snake.t[i].Y)
				return FINISH;
	}
	
	if(next.X == game.apple.X && next.Y == game.apple.Y)
	{
		COORD *temp = new COORD[++count];
		for(int i = 0; i < count; i++) temp[i] = game.snake.t[i];
		temp[count - 1] = next;
		delete [] game.snake.t;
		game.snake.t = temp;
		
		SetConsoleCursorPosition(hConsole, head);
		SetConsoleTextAttribute(hConsole, 0x0a);
		cout << "*";
		SetConsoleCursorPosition(hConsole, next);
		SetConsoleTextAttribute(hConsole, 0x0a);
		printf("%c", 1);
		AddApple(game);

		return APPLE;
	}
	
	for(int i = 0; i < count - 1; i++) game.snake.t[i] = game.snake.t[i + 1];

	game.snake.t[count - 1] = next;
	SetConsoleCursorPosition(hConsole, tail);
	cout << " ";
	
	SetConsoleCursorPosition(hConsole, head);
	SetConsoleTextAttribute(hConsole, 0x0a);
	cout << "*";
	SetConsoleCursorPosition(hConsole, next);
	SetConsoleTextAttribute(hConsole, 0x0f);
	printf("%c",1);
	
	return MOVE;
}

void Menu()
{
	SetConsoleTitle(L"SNAKE");
	system ("mode con cols=45 lines=10");
	cout << "\t\tSNAKE GAME" << endl << endl;
	cout << "\tYour goal : eat 50 apples and win!!!" << endl << endl;
    cout << "\tPress any key to continue..." << endl;
	getch();
}

int main()
{
	srand(time(0));
	
	Menu();

	system ("mode con cols=100 lines=40");

    int key = 0, count = 0;

	bool IsPaused = false;
	Game g;
	StartGame(g);

	Show();

	while(key != 27)
	{
		while(!kbhit()) 
		{
			if(IsPaused)
			{
				Sleep(1);
				continue;
			}
			
			switch (Move(g))
			{
				case APPLE:
					++count;
					--g.speed;

					SetColor(LightBlue, Black);

					MoveCursor(64, 2); cout <<  "STATS" << endl;
					MoveCursor(64, 3); cout << "\tApples: " << count << endl;
					MoveCursor(64, 4); cout << "\tLength: " << g.snake.Length << endl;
					MoveCursor(64, 5); cout << "\tSpeed:  " << 101 - g.speed << endl;
					MoveCursor(64, 7); cout << "Controls" << endl;
					MoveCursor(64, 8); cout << "\tEsc - Exit" << endl;
					MoveCursor(64, 9); cout << "\tP   - Pause/Unpause" << endl;
					MoveCursor(64, 12);
					cout << "\t" << "Arrow Up    - Turn Up" << endl;
					MoveCursor(64, 13);
					cout << "\t" << "Arrow Down  - Turn Down" << endl;
					MoveCursor(64, 14);
					cout << "\t" << "Arrow Left  - Turn Left" << endl;
					MoveCursor(64, 15);
					cout << "\t" << "Arrow Right - Turn Right" << endl;
					
					if (count == 50) 
					{
						SetColor(White, Black);
						MoveCursor(30, 30);
						cout << "You Win!!!" << endl;
						MoveCursor(30, 31);
						getch();
						return 0;
					}
					break;
				
			case WALL:
			case FINISH:
				MoveCursor(30, 30);
				cout << "GAME OVER!!!\n";
				MoveCursor(30, 31);
				getch();
				return 0;
			}
			
			Sleep(g.speed);
		}

		key = getch();
		
    	if(key == 'p' || key == 'P' || key == 135 || key == 167) IsPaused = !IsPaused;
		else if(key == VK_ESCAPE) 
		{
			MoveCursor(30, 30);
			exit(0);	
		}
		else if(key == 0 || key == 224)
		{
			key = getch();
			
			if(key == 72 && g.direct != DOWN)
			{
				g.direct = UP;
				g.dx = 0;
				g.dy = -1;
			}
			else if(key == 80 && g.direct != UP)
			{
				g.direct = DOWN;
				g.dx = 0;
				g.dy = 1;
			}
			else if(key == 75 && g.direct != RIGHT)
			{
				g.direct = LEFT;
				g.dx = -1;
				g.dy = 0;
			}
			else if(key == 77 && g.direct != LEFT)
			{
				g.direct = RIGHT;
				g.dx = 1;
				g.dy = 0;
			}
		}
	}
}